package service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import model.command.Command;
import model.command.CommandPos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import utils.CommandUtil;
import utils.LoggerUtil;
import utils.RandomAccessFileUtil;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.TreeMap;

public class MyThread extends Thread {
    public MyThread() {
        this.memTable = new TreeMap<String, Command>();
        this.index = new HashMap<>();
    }

    public static final String RW_MODE = "rw";
    private final Logger LOGGER = LoggerFactory.getLogger(NormalStore.class);
    private final String logFormat = "[NormalStore][{}]: {}";

    private static final int maxStoreFileSize = 50; // 50B(最大可存储容量)
    //    private static final int maxStoreFileSize = 1024 * 10 - 50; // 10 - 50KB(最大可存储容量)
    private final String filePath = "data/data"; // 基地址
    public static final String TABLE = ".table";

    /**
     * 利用TreeMap的特性来进行对文件的压缩操作
     */
    private TreeMap<String, Command> memTable;

    /**
     * hash索引，存的是数据长度和偏移量
     */
    private HashMap<String, CommandPos> index;


    @Override
    public void run() {

        try {
            reloadFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 将全部文件的数据读进treeMap中，利用treeMap的特性去重
    public void reloadFile() throws IOException {
        int filePathNum = 1; // 控制是哪一个文件

        while (true) {
            try {
                // 判断该文件是否存在，不存在则结束该方法
                File files = new File(this.filePath + filePathNum + TABLE);
                if(!files.exists()) {
                    break;
                }

                // 以读写模式打开文件
                RandomAccessFile file = new RandomAccessFile(this.filePath + filePathNum + TABLE, RW_MODE);
                // 获取文件长度
                long len = file.length();
                // 从文件开头开始读取
                long start = 0;
                file.seek(start);

                // 逐个读取文件中的命令
                while (start < len) {
                    // 读取命令长度
                    int cmdLen = file.readInt();

                    // 创建字节数组读取命令内容
                    byte[] bytes = new byte[cmdLen];
                    file.read(bytes);
                    // 将字节数组解析为JSON对象
                    JSONObject value = JSON.parseObject(new String(bytes, StandardCharsets.UTF_8));
                    // 将JSON对象转换为Command对象
                    Command command = CommandUtil.jsonToCommand(value);

                    // 如果成功解析了命令
                    // 保存到memTable
                    if (command != null) {
                        memTable.put(command.getKey(), command);
                    }

                    // 记录当前命令在文件中的起始位置
                    start += 4;

                    // 移动到下一个命令的起始位置
                    start += cmdLen;

                }
                // 将文件指针移动到文件末尾
                file.seek(file.length());

                // 清空文件内容，以便后续重新输入
                clearInfoForFile(this.filePath + filePathNum + TABLE);

                filePathNum += 1;

            } catch (Exception e) {
                // 捕获任何异常并打印栈trace
                e.printStackTrace();
            }

            // 记录日志,输出重新加载索引的结果
            LoggerUtil.debug(LOGGER, logFormat, "reload index: " + index.toString());
        }

        // 将treeMap里的东西写进磁盘文件中
        flushMemTableToDisk();
    }

    private void flushMemTableToDisk() throws IOException {
        try {
            int filePathNum = 1; // 控制是哪一个文件

            // 遍历memTable中的所有键
            for (String key : memTable.keySet()) {
                // 获取与当前键关联的Command对象
                Command command = memTable.get(key);

                // 使用JSON序列化将Command对象转换为字节数组
                byte[] commandBytes = JSONObject.toJSONBytes(command);

                // 将命令字节的长度写入文件
                RandomAccessFileUtil.writeInt(this.filePath + filePathNum + TABLE, commandBytes.length);

                // 将命令字节写入文件,并获取写入的位置
                RandomAccessFileUtil.write(this.filePath + filePathNum + TABLE, commandBytes);

                File files = new File(this.filePath + filePathNum + TABLE);

                //如果文件大小大于maxStoreFileSize，则切换下一个文件
                if (files.length() >= maxStoreFileSize) {
                    filePathNum += 1;
                }
            }

            // 将memTable的内容刷新到磁盘后,清空memTable
            memTable.clear();


        } catch (RuntimeException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 清空文件内容
     */
    public void clearInfoForFile(String fileName) {
        File file = new File(fileName);
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write("");
            fileWriter.flush();
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

